﻿#include "application.h"
#include <xy/utils/scope_profiler.h>

#include <xy/render/raytracer.h>

using namespace xy::render;
using namespace xy::geom;
using namespace xy::utils;

struct my_context : public xy::render::context
{
    std::shared_ptr<point_shader> pointShader;     // 点着色器
    std::shared_ptr<line_shader> lineShader;       // 线着色器
    std::shared_ptr<triangle_shader> meshShader;   // 网格着色器
    std::shared_ptr<darboux_shader> darbouxShader; // 网格着色器

    my_context(int width, int height, int msaaLevel) : xy::render::context(width, height, msaaLevel)
    {
    }

    void draw() override
    {
        m_painter->clear_color(0xff000000);
        m_painter->clear_depth(0xffffffff);

        {
            meshShader->uniform.projection = m_camera->projection();
            meshShader->uniform.vp = m_camera->projection() * m_camera->view();
            meshShader->uniform.viewPos = m_camera->position();
            renderer::draw(m_painter, *meshShader);
        }

        // {
        //     darbouxShader->uniform.vp = m_camera->projection * m_camera->camera.view();
        //     renderer::draw(m_painter, *darbouxShader);
        // }

        {
            lineShader->uniform.vp = m_camera->projection() * m_camera->view();
            renderer::draw(m_painter, *lineShader);
        }

        {
            pointShader->uniform.vp = m_camera->projection() * m_camera->view();
            renderer::draw(m_painter, *pointShader);
        }
    }
};

void ray_render()
{
    int w = 512;
    int h = 512;
    image_t image(w, h, 4);

    whited_style_raytracer raytracer;

    // raytracer.uniform.viewPos = vec3f{0.0f, 10.0f, 0.0f};
    raytracer.uniform.viewDir = vec3f{0.0f, -1.0f, -10.0f}.normalize();

    auto &objects = raytracer.objects;
    objects.emplace_back(vec3f(0.0f, 0.0f, -10.0f), 0.5f, primitive_material{color_t(255, 0, 0, 255), 0.5f, 0.0f});
    // objects.emplace_back(vec3f(-2.0f, -2.0f, -10.0f), 2.0f, primitive_material{color_t(0, 255, 0, 255), 0.5f, 0.0f});
    objects.emplace_back(vec3f(3.0f, 0.0f, -18.0f), 2.0f, primitive_material{color_t(0, 0, 255, 255), 0.1f, 1.0f});
    objects.emplace_back(vec3f(-3.0f, 0.0f, -16.0f), 2.0f, primitive_material{color_t(255, 0, 0, 255), 0.5f, 0.0f});
    objects.emplace_back(vec3f(0.0f, -10000.0f, 0.0f), 9995.0f, primitive_material{color_t{0xff65CB51}, 0.5f, 0.0f});

    auto &lights = raytracer.lights;
    // lights.emplace_back(vec3f(-10.0f, -10.0f, -10.0f), 0.1f, 0.8f, 0.1f);
    lights.emplace_back(vec3f(1.0f, 5.0f, 0.0f), 1.0f, 1.0f);

    renderer::draw(raytracer, image);

    image.flip_vertically();
    image.write(TEST_DATA_PATH "ray.png");
}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // ray_render();
    // return 0;

    xy::app::application app(hInstance, nCmdShow, 100, 100, 800, 800, L"My Demo");

    auto renderContext = app.register_render_context<my_context>(1);
    auto aisContext = app.register_ais_context<xy::ais::context>();

    {
        auto program = std::make_shared<point_shader>();

        program->varyings.emplace_back(vec3f{-0.5f, -0.5f, 0.0f}, vec3f{1.0f, 0.0f, 0.0f});
        program->varyings.emplace_back(vec3f{-0.5f, 0.5f, 0.0f}, vec3f{0.0f, 1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{0.5f, 0.5f, 0.0f}, vec3f{0.0f, 0.0f, 1.0f});
        program->varyings.emplace_back(vec3f{0.5f, -0.5f, 0.0f}, vec3f{1.0f, 1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{1.5f, -0.5f, 0.0f}, vec3f{1.0f, 1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{0.0f, 0.0f, 5.0f}, vec3f{1.0f, 1.0f, 1.0f});

        renderContext->pointShader = program;
    }

    {
        auto program = std::make_shared<line_shader>();

        program->varyings.emplace_back(vec3f{-0.5f, -0.5f, 0.0f}, vec3f{1.0f, 0.0f, 0.0f});
        program->varyings.emplace_back(vec3f{-0.5f, 0.5f, 0.0f}, vec3f{0.0f, 1.0f, 0.0f});

        program->varyings.emplace_back(vec3f{-0.5f, 0.5f, 0.0f}, vec3f{0.0f, 1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{0.5f, 0.5f, 0.0f}, vec3f{0.0f, 0.0f, 1.0f});

        program->varyings.emplace_back(vec3f{0.5f, 0.5f, 0.0f}, vec3f{0.0f, 0.0f, 1.0f});
        program->varyings.emplace_back(vec3f{0.5f, -0.5f, 0.0f}, vec3f{1.0f, 1.0f, 0.0f});

        program->varyings.emplace_back(vec3f{0.5f, -0.5f, 0.0f}, vec3f{1.0f, 1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{-0.5f, -0.5f, 0.0f}, vec3f{1.0f, 0.0f, 0.0f});

        program->varyings.emplace_back(vec3f{0.5f, -0.5f, 0.0f}, vec3f{1.0f, 1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{1.5f, -0.5f, 0.0f}, vec3f{1.0f, 0.0f, 0.0f});

        renderContext->lineShader = program;
    }

    {
        mesh m;
        // m.read(TEST_DATA_PATH "centaur/body.obj");
        m.read(TEST_DATA_PATH "tinyrenderer/african_head/african_head.obj");

        auto program = std::make_shared<triangle_shader>();
        for (std::size_t r = 0; r < m->indices.size(); r++)
        {
            std::size_t i = m->indices[r];
            program->varyings.emplace_back(m->vertices[i], m->normals[i], m->texcoords[i]);
        }

        float w = 2.5f;
        program->varyings.emplace_back(vec3f{-w, -w, -10.0f}, vec3f{0.0f, 0.0f, 1.0f}, vec2f{0.0f, 0.0f});
        program->varyings.emplace_back(vec3f{w, -w, -10.0f}, vec3f{0.0f, 0.0f, 1.0f}, vec2f{1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{-w, w, -10.0f}, vec3f{0.0f, 0.0f, 1.0f}, vec2f{0.0f, 1.0f});

        program->varyings.emplace_back(vec3f{-w, w, -10.0f}, vec3f{0.0f, 0.0f, 1.0f}, vec2f{0.0f, 1.0f});
        program->varyings.emplace_back(vec3f{w, -w, -10.0f}, vec3f{0.0f, 0.0f, 1.0f}, vec2f{1.0f, 0.0f});
        program->varyings.emplace_back(vec3f{w, w, -10.0f}, vec3f{0.0f, 0.0f, 1.0f}, vec2f{1.0f, 1.0f});

        // program->uniform.mode = program->uniform.normal;
        // program->uniform.mode = program->uniform.wireframe;
        // program->uniform.mode = program->uniform.ssao;

        auto gridMap = TEST_DATA_PATH "tinyrenderer/grid.tga";
        renderContext->create_texture(gridMap);
        renderContext->create_texture(xy::render::context::Chess);

        // auto diffuseMap = TEST_DATA_PATH "centaur/body_diffuse.tga";
        auto diffuseMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_diffuse.tga";
        // auto diffuseMap = TEST_DATA_PATH "tinyrenderer/floor_diffuse.tga";
        renderContext->create_texture(diffuseMap);

        // auto specularMap = TEST_DATA_PATH "centaur/body_specular.tga";
        auto specularMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_spec.tga";
        renderContext->create_texture(specularMap);

        auto normalMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_nm.tga";
        renderContext->create_texture(normalMap);

        auto tangentMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_nm_tangent.tga";
        renderContext->create_texture(tangentMap);

        // program->uniform.diffuseMap = renderContext->texture(gridMap);
        program->uniform.diffuseMap = renderContext->texture(diffuseMap);
        // program->uniform.diffuseMap = renderContext->texture("Chess");
        program->uniform.specularMap = renderContext->texture(specularMap);
        // program->uniform.normalMap = renderContext->texture(normalMap);
        program->uniform.normalMap = renderContext->texture(tangentMap);
        program->uniform.light.position = vec3f{0.0f, 0.0f, 5.0f};
        program->uniform.light.direction = vec3f{0.0f, -0.001f, -0.5f}.normalize();
        program->uniform.light.diffuse = 0.8f;
        program->uniform.light.specular = 0.1f;

        renderContext->meshShader = program;
    }

    // {
    //     mesh m;
    //     m.read(TEST_DATA_PATH "tinyrenderer/african_head/african_head.obj");

    //     auto program = std::make_shared<darboux_shader>();
    //     for (std::size_t r = 0; r < m->indices.size(); r++)
    //     {
    //         std::size_t i = m->indices[r];
    //         program->varyings.emplace_back(m->vertices[i], m->normals[i], m->texcoords[i]);
    //     }

    //     auto gridMap = TEST_DATA_PATH "tinyrenderer/grid.tga";
    //     renderContext->create_texture(gridMap);

    //     auto diffuseMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_diffuse.tga";
    //     renderContext->create_texture(diffuseMap);

    //     auto specularMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_spec.tga";
    //     renderContext->create_texture(specularMap);

    //     auto normalMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_nm.tga";
    //     renderContext->create_texture(normalMap);

    //     auto tangentMap = TEST_DATA_PATH "tinyrenderer/african_head/african_head_nm_tangent.tga";
    //     renderContext->create_texture(tangentMap);

    //     renderContext->darbouxShader = program;
    // }

    return app.message_loop();
}
